package com.gopay.bis.spdb.expay.impl;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.gopay.bis.common.util.BankLogUtils;
import com.gopay.bis.spdb.adpay.operation.CryptoHelper;
import com.gopay.bis.spdb.adpay.operation.SocketTransport;
import com.gopay.bis.spdb.adpay.operation.XmlBeanUtil;
import com.gopay.common.cipher.utils.LoggerMaskUtils;
import com.gopay.common.constants.bank.SpdbConstants;
import com.gopay.common.constants.proccode.ProcCodeConstants;
import com.gopay.common.domain.bank.BaseMessage;
import com.gopay.common.domain.bank.ExPayBaseMessage;
import com.gopay.common.domain.bank.spdb.adpay.SpdbAdPayBody;
import com.gopay.common.domain.bank.spdb.adpay.SpdbAdPayHeader;
import com.gopay.common.domain.bank.spdb.adpay.SpdbAdPayMessage;
import com.gopay.common.domain.bank.spdb.adpay.req.AcntCheckReq;
import com.gopay.common.domain.bank.spdb.adpay.req.SinContractReq;
import com.gopay.common.domain.bank.spdb.adpay.req.SinCutQueryReq;
import com.gopay.common.domain.bank.spdb.adpay.req.SinCutReq;
import com.gopay.common.domain.bank.spdb.adpay.res.AcntCheckRes;
import com.gopay.common.domain.bank.spdb.adpay.res.SignInRes;
import com.gopay.common.domain.bank.spdb.adpay.res.SinContractRes;
import com.gopay.common.domain.bank.spdb.adpay.res.SinCutQueryRes;
import com.gopay.common.domain.bank.spdb.adpay.res.SinCutRes;
import com.gopay.common.exception.GopayException;
import com.gopay.common.util.DateUtils;
import com.gopay.remote.bank.businesses.ExPayService;
import com.gopay.remote.bank.businesses.SingInService;
import com.tongtech.wtp.WtpDownloadFile;

/**
 * 浦发代扣
 * 
 * SpdbAdPayServiceImpl.java
 * @author jiangxiangjiao @2016-11-2
 */
@SuppressWarnings("all")
public class SpdbAdPayServiceImpl implements ExPayService, SingInService {

	private final static Logger logger = LoggerFactory.getLogger(SpdbAdPayServiceImpl.class);
	
	private static final String SPDB_AES_KEY = "spdbAESKey";

	private SocketTransport socketTransport;
	private CryptoHelper spdbCipher;
	private Map<String, String> encryptKeyCache = new HashMap<String, String>();
	
//	private String AESKeyPath;
	
	@Override
	public Object certification(BaseMessage baseMsg) throws GopayException {
		try {
			SpdbAdPayMessage<SpdbAdPayBody<AcntCheckReq>> transMsg = (SpdbAdPayMessage<SpdbAdPayBody<AcntCheckReq>>) baseMsg;
			AcntCheckReq sinContract =  transMsg.getBody().getInfo();
			// 生成待加密的info明文
			String infoXmlPlainText = Obj2Xml(sinContract, AcntCheckReq.class);
			String keyValue = getKey(baseMsg.getPrivateKeyPath2nd());  
			String encryptMsg = spdbCipher.encryCipherByAES(infoXmlPlainText, keyValue);
//			System.out.println("encryptMsg======" + encryptMsg);
			String reqXml = XmlBeanUtil.SpdbAdPayMessageToXml(transMsg, encryptMsg);
			logger.error("身份核实请求报文:"+ reqXml);
			String len = getXmlLen(reqXml);
			
			String xml = submit(len, reqXml, baseMsg.getPayUrl());
			SpdbAdPayMessage<String> result = XmlBeanUtil.toSpdbAdPayMessage(xml);
			if(!StringUtils.equals(SpdbConstants.RESPCODE_SUCCESS, result.getHeader().getRespCode())){
				SpdbAdPayMessage<SpdbAdPayBody> res = new SpdbAdPayMessage<SpdbAdPayBody>();
				res.setHeader(result.getHeader());
				res.setBody(null);
				return res;
			}else{
				String infoXml = spdbCipher.decryCipherByAES(result.getBody().toString(), keyValue);
				AcntCheckRes info = XmlBeanUtil.toBean(infoXml, AcntCheckRes.class);
				return getReturnMsg(info, result,false);
			}
		} catch (Exception e) {
			logger.error("认证异常",e);
			throw new GopayException(ProcCodeConstants.PROC_CODE_100E6076,e);
		}
		
	}

	@Override
	public Object payment(BaseMessage baseMsg) throws GopayException {
		try {
			SpdbAdPayMessage<SpdbAdPayBody<SinCutReq>> transMsg = (SpdbAdPayMessage<SpdbAdPayBody<SinCutReq>>) baseMsg;
			SinCutReq sinCutReq =  transMsg.getBody().getInfo();
			// 生成待加密的info明文
			String infoXmlPlainText = Obj2Xml(sinCutReq, SinCutReq.class);
			String keyValue = getKey(baseMsg.getPrivateKeyPath2nd());  
			String encryptMsg = spdbCipher.encryCipherByAES(infoXmlPlainText, keyValue);
			String reqXml = XmlBeanUtil.SpdbAdPayMessageToXml(transMsg, encryptMsg);
			logger.error("单笔代扣请求报文:"+ reqXml);
			String len = getXmlLen(reqXml);
			
			String xml = submit(len, reqXml, baseMsg.getPayUrl());
			SpdbAdPayMessage<String> result = XmlBeanUtil.toSpdbAdPayMessage(xml);
			if(!StringUtils.equals(SpdbConstants.RESPCODE_SUCCESS, result.getHeader().getRespCode())){
				SpdbAdPayMessage<SpdbAdPayBody> res = new SpdbAdPayMessage<SpdbAdPayBody>();
				res.setHeader(result.getHeader());
				res.setBody(null);
				res.setBankRetCode(result.getHeader().getRespCode()); //新增错误码
				res.setReturnInfo(result.getHeader().getRespDesc()); //新增错误信息
				return res;
			}else{
				String infoXml = spdbCipher.decryCipherByAES(result.getBody().toString(), keyValue);
				SinCutRes info = XmlBeanUtil.toBean(infoXml, SinCutRes.class);
				return getReturnMsg(info, result,true);
			}
		} catch (Exception e) {
			logger.error("支付异常",e);
			throw new GopayException(ProcCodeConstants.PROC_CODE_100E6057,e);
		}
		
	}

	@Override
	public Object queryOrder(BaseMessage baseMsg) throws GopayException {
		try {
			SpdbAdPayMessage<SpdbAdPayBody<SinCutQueryReq>> transMsg = (SpdbAdPayMessage<SpdbAdPayBody<SinCutQueryReq>>) baseMsg;
		    SinCutQueryReq sinCutQueryReq =  transMsg.getBody().getInfo();
	        // 生成待加密的info明文
	        String infoXmlPlainText = Obj2Xml(sinCutQueryReq, SinCutQueryReq.class);
	        String keyValue = getKey(baseMsg.getPrivateKeyPath2nd()); 
	        String encryptMsg = spdbCipher.encryCipherByAES(infoXmlPlainText, keyValue);
	        String reqXml = XmlBeanUtil.SpdbAdPayMessageToXml(transMsg, encryptMsg);
	        logger.info("单笔代扣请求报文:"+ reqXml);
	        String len = getXmlLen(reqXml);
	        
	        String xml = submit(len, reqXml, baseMsg.getPayUrl());
	        SpdbAdPayMessage<String> result = XmlBeanUtil.toSpdbAdPayMessage(xml);
	        if(!StringUtils.equals(SpdbConstants.RESPCODE_SUCCESS, result.getHeader().getRespCode())){
	            SpdbAdPayMessage<SpdbAdPayBody> res = new SpdbAdPayMessage<SpdbAdPayBody>();
	            res.setHeader(result.getHeader());
	            res.setBody(null);
	            return res;
	        }else{
	            String infoXml = spdbCipher.decryCipherByAES(result.getBody().toString(), keyValue);
	            SinCutQueryRes info = XmlBeanUtil.toBean(infoXml, SinCutQueryRes.class);
	            return getReturnMsg(info, result,false);
	        }
		} catch (Exception e) {
			logger.error("对账查询异常",e);
			throw new GopayException(ProcCodeConstants.PROC_CODE_100E6058,e);
		}
	    
	}

	@Override
	public Object treatyQuery(BaseMessage baseMsg) throws GopayException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Object cancel(BaseMessage baseMsg) throws GopayException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Object refund(BaseMessage baseMsg) throws GopayException {
		// TODO Auto-generated method stub
		return null;
	}

	/**
	 * 签到交易
	 * @param msg
	 * return Object
	 * @throws GopayException
	 */
	@Override
	public Object signIn(Object msg) throws GopayException {
		try {
			SpdbAdPayMessage<SpdbAdPayBody> transMsg = (SpdbAdPayMessage<SpdbAdPayBody>) msg;
			String req = XmlBeanUtil.SpdbAdPayMessageToXml(transMsg, null);
			String len = getXmlLen(req);
			logger.error(new DateTime().toString("YYYYMMdd hh:mm:ss") + "浦发代扣签到---获取密钥 ");
			
			String xml = submit(len, req, transMsg.getPayUrl());
			if(StringUtils.isNotEmpty(xml) && xml.indexOf("Error") >= 0){
				logger.error("签到交易异常:" + xml);
				throw new GopayException(ProcCodeConstants.PROC_CODE_100E6076);
			}
			SpdbAdPayMessage<String> result = XmlBeanUtil.toSpdbAdPayMessage(xml);
			
			if(!StringUtils.equals(SpdbConstants.RESPCODE_SUCCESS, result.getHeader().getRespCode())){
				SpdbAdPayMessage<SpdbAdPayBody> res = new SpdbAdPayMessage<SpdbAdPayBody>();
				res.setHeader(result.getHeader());
				res.setBody(null);
				return res;
			}else{
				logger.info("签到获取的密钥  : " + result.getBody().toString());
				SignInRes info = XmlBeanUtil.toBean(result.getBody().toString(), SignInRes.class);
				// 获取密钥
				String encryptKey = info.getKeyValue();
				// 解密密钥
				byte [] encryptBytes = spdbCipher.decryptKey(encryptKey,
						transMsg.getPrivateKeyPath(), transMsg.getPrivatePwd(), transMsg.getPrivatePwd2nd());
				
				String base64Key = spdbCipher.keyByteToString(encryptBytes);
				logger.error("密钥明文："+ base64Key);
				// 将Base64密钥存入缓存
				encryptKeyCache.put(SPDB_AES_KEY, base64Key);
				info.setKeyValue(base64Key);
				return getReturnMsg(info, result,false);
			}
		} catch (Exception e) {
			logger.error("签到交易异常",e);
			throw new GopayException(ProcCodeConstants.PROC_CODE_100E6076,e);
		}
	}

	@Override
	public Object sendSms(BaseMessage baseMsg) throws GopayException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Object checkSms(BaseMessage baseMsg) throws GopayException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Object queryCvn(BaseMessage baseMsg) throws GopayException {
		// TODO Auto-generated method stub
		return null;
	}

	 /**
     * 代扣签约
     * @param baseMsg
     * @return
     * @throws GopayException
     */
	@Override
	public Object sign(BaseMessage baseMsg) throws GopayException {
		try {
			SpdbAdPayMessage<SpdbAdPayBody<SinContractReq>> transMsg = (SpdbAdPayMessage<SpdbAdPayBody<SinContractReq>>) baseMsg;
			SinContractReq sinContract =  transMsg.getBody().getInfo();
			// 生成待加密的info明文
			String infoXmlPlainText = Obj2Xml(sinContract, SinContractReq.class);
			String keyValue = getKey(baseMsg.getPrivateKeyPath2nd()); 
			String encryptMsg = spdbCipher.encryCipherByAES(infoXmlPlainText, keyValue);
			String reqXml = XmlBeanUtil.SpdbAdPayMessageToXml(transMsg, encryptMsg);
			logger.info("代扣签约请求报文:"+ reqXml);
			String len = getXmlLen(reqXml);
			
			String xml = submit(len, reqXml, baseMsg.getPayUrl());
			SpdbAdPayMessage<String> result = XmlBeanUtil.toSpdbAdPayMessage(xml);
			if(!StringUtils.equals(SpdbConstants.RESPCODE_SUCCESS, result.getHeader().getRespCode())){
				SpdbAdPayMessage<SpdbAdPayBody> res = new SpdbAdPayMessage<SpdbAdPayBody>();
				res.setHeader(result.getHeader());
				res.setBody(null);
				return res;
			}else{
				String infoXml = spdbCipher.decryCipherByAES(result.getBody().toString(), keyValue);
				SinContractRes info = XmlBeanUtil.toBean(infoXml, SinContractRes.class);
				return getReturnMsg(info, result,false);
			}
		} catch (Exception e) {
			logger.error("签约异常",e);
			throw new GopayException(ProcCodeConstants.PROC_CODE_100E6053,e);
		}
		
	}

	@Override
	public Object verify(BaseMessage baseMsg) throws GopayException {
		// TODO Auto-generated method stub
		return null;
	}
	
	private String Obj2Xml(Object obj, Class clazz){
		String infoXmlPlainText = XmlBeanUtil.toXml(obj, clazz);
		logger.error("等待加密报文：" + LoggerMaskUtils.maskString(infoXmlPlainText));
		return infoXmlPlainText;
	}
	
	/**
	 * 提交Socket请求
	 * @param len 报文长度的字符串表示
	 * @param reqXml 请求报文
	 * @throws UnsupportedEncodingException 
	 * @throws GopayException 
	 */
	public String submit(String len,String reqXml, String url) throws UnsupportedEncodingException {
		if(StringUtils.isNotEmpty(url)){
			String []arr = url.split(":");
			socketTransport.setHost(arr[0]);
			socketTransport.setPort(Integer.parseInt(arr[1]));
		}
		
		String resMsg = socketTransport.submit(reqXml);
		/*List<byte[]> srcArrays = new ArrayList<byte[]>();
		srcArrays.add(len.getBytes("gbk"));
		srcArrays.add(reqXml.getBytes("gbk"));
		logger.error("send reqXml:" + reqXml);
		String resMsg = socketTransport.submit(sysCopy(srcArrays));*/
//		logger.error("receive byte[]:" + Arrays.toString(resMsg));
		
//		String xml = parseResponseByte(resMsg);
		logger.error("receive data:" + resMsg);
		return resMsg;
	}
	
	/**
	 * 返回最终的SpdbAdPayMessage<T>对象
	 * @param src body为String类型的SpdbAdPayMessage
	 * @param info 
	 * @param payFlag 新增参数--支付标记，如果是支付操作，返回相关交易码及对应描述
	 */
	public <T> SpdbAdPayMessage<SpdbAdPayBody<T>>  getReturnMsg(T info,SpdbAdPayMessage<String> src,boolean payFlag){
		SpdbAdPayMessage<SpdbAdPayBody<T>> res = new SpdbAdPayMessage<SpdbAdPayBody<T>>();
		res.setHeader(src.getHeader());
		SpdbAdPayBody<T> body = new SpdbAdPayBody<T>();
		body.setInfo(info);
		res.setBody(body);
		if(payFlag){
			res.setBankRetCode(((SinCutRes)info).getRespCode());
			res.setReturnInfo(((SinCutRes)info).getRespDesc());
		}
		return res;
	}
	
	/**
	 * 从Map缓存中获取密钥,若密钥不存在则从文件中获取，并存如Map缓存
	 */
	private String getKey(String AESKeyPath){
//		System.out.println("AESKeyPath======" + AESKeyPath);
		String key = encryptKeyCache.get(SPDB_AES_KEY);
		if(!StringUtils.isBlank(key)){
			return key;
		}else{
			StringBuffer sb = new StringBuffer();
			BufferedReader br = null;
			try {
				br = new BufferedReader(new FileReader(new File(AESKeyPath)));
				String line = null;
				while((line = br.readLine()) != null){
					sb.append(line.trim());
				}
				encryptKeyCache.put(SPDB_AES_KEY, sb.toString());
			} catch (IOException e) {
				logger.error(new DateTime().toString("YYYYMMdd hh:mm:ss")+"浦发代扣-读取密钥文件异常",e);
			}finally{
				if(br != null){
					try {
						br.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
			return sb.toString();
		}
	}
	
	/**
	 * 处理银行响应,返回带有加密信息的xml报文
	 * @param resMsg 响应字节数组
	 */
	public String parseResponseByte(byte[] resMsg){
		String xmlMsg = "";
		try {
			// 前8位报文总长
//			System.out.println("==========" + new String(resMsg,"GBK"));
			byte[] xmlLen = new byte[8]; 
			System.arraycopy((byte[]) resMsg, 0, xmlLen, 0, 8);
			int leng = Integer.parseInt(new String(xmlLen, "GBK").replaceFirst("^0*", ""));
			// 报文体字节数组
			byte [] body = new byte [resMsg.length - 8];
			System.arraycopy((byte[]) resMsg, 8, body, 0, resMsg.length-8);
			xmlMsg = new String(body,"GBK");
			logger.info("响应报文总长度为:" + leng +",未解密的报文明文为:"+xmlMsg);
		} catch (NumberFormatException | UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return xmlMsg;
	}
	
	

	/**
	 * 获取8位报文长度(右对齐左补零)
	 * 
	 * @param reqXml
	 * @param bank
	 * @return
	 */
	private String getXmlLen(String reqXml) {
		try {
			// 报文长度
			String xmlLength = "";
			//String xml = reqXml.replaceAll("\n", "").replaceAll("\r", "").replaceAll(" ", "");
			xmlLength = String.valueOf(reqXml.getBytes("gbk").length);
			if(xmlLength.length() < 8){
				xmlLength = StringUtils.repeat("0", 8-xmlLength.length()) +  xmlLength;
			}
			return xmlLength;
		} catch (UnsupportedEncodingException e) {
			BankLogUtils.printManagerExceptionLog(e);
			return null;
		}
	}
	
	/**
	 * 系统提供的数组拷贝方法arraycopy
	 */
	public byte[] sysCopy(List<byte[]> srcArrays) {
		int len = 0;
		for (byte[] srcArray : srcArrays) {
			len += srcArray.length;
		}
		byte[] destArray = new byte[len];
		int destLen = 0;
		for (byte[] srcArray : srcArrays) {
			System.arraycopy(srcArray, 0, destArray, destLen, srcArray.length);
			destLen += srcArray.length;
		}
		logger.error("SpdbAdPayServiceImpl sysCopy length: " + destArray.length);
		return destArray;
	}
	
	public static void main(String argv[])
    {
		AcntCheckReq acntCheck = new AcntCheckReq();
		acntCheck.setReqDate(new DateTime().toString("YYYYMMdd"));
        // 生成唯一系统流水号
        String serialNo = DateUtils.getLocalFullDateTime14().substring(0, 8);
        acntCheck.setReqTransID(serialNo);
        acntCheck.setIsVali("0");
		// 生成待加密的info明文
		String infoXmlPlainText = XmlBeanUtil.toXml(acntCheck, AcntCheckReq.class);
		System.out.println(infoXmlPlainText);
		 /*SpdbAdPayHeader header = new SpdbAdPayHeader();
	        header.setActionCode("0");
	        header.setActivityCode("DaliySignKey");
	        // 请求渠道号 00-人工渠道;02-电子渠道;99-默认渠道
	        header.setReqChannelNo("02");
	        //请求商户号 由浦发侧受托支付系统生成,需商户在银行侧进行签约入网登记
	        header.setReqMerchantNo("9900000415");
	        header.setReqDateTime(new DateTime().toString("YYYYMMddhhmmss"));
	        // 生成唯一系统流水号
	        header.setRespSysID("");

	        SpdbAdPayBody <String>body = new SpdbAdPayBody<String>();
	        SpdbAdPayMessage<SpdbAdPayBody<String>> reqMsg = new SpdbAdPayMessage<SpdbAdPayBody<String>>();
//	        reqMsg.setBody(body);
	        reqMsg.setHeader(header);
	        
		SpdbAdPayServiceImpl i = new SpdbAdPayServiceImpl();
		String reqXml = XmlBeanUtil.SpdbAdPayMessageToXml(reqMsg, null);
//		String reqXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><STPContext><Header><ActionCode>0</ActionCode><ActivityCode>DaliySignKey</ActivityCode><ReqChannelNo>02</ReqChannelNo><ReqMerchantNo>9900000415</ReqMerchantNo><ReqDateTime>20170724113100</ReqDateTime><RespSysID>20170724RmZElT7p</RespSysID></Header><Body><![CDATA[]]></Body></STPContext>";
		String len = i.getXmlLen(reqXml );
		System.out.println(reqXml);
		System.out.println(len);*/
        /*WtpDownloadFile download = new WtpDownloadFile();
        
        download.setOverWrite(true);  // 默认覆盖,
        download.setLocalFileDir(".\\download8"); // 设置

        if ( download.downLoadFile("aaa.bak") !=0  )
            download.printErrorMsg();
                System.out.println("Java:TransId=["+download.getTransId()+"]");                //返回值
                System.out.println("Java:LocalFileName=["+download.getLocalFileName()+"]");      //返回值

        if ( download.downLoadFile("S???.txt") !=0  )
            download.printErrorMsg();

            System.out.println("Java:TransId=["+download.getTransId()+"]");                //返回值
                System.out.println("Java:LocalFileName=["+download.getLocalFileName()+"]");      //返回值

                 download.setLocalFileDir(null); // 不设置,默认为配置项为准

         if ( download.downLoadFile("14495c4682","cccc.bak") !=0  )
             download.printErrorMsg();

             System.out.println("Java:TransId=["+download.getTransId()+"]");                //返回值
                 System.out.println("Java:LocalFileName=["+download.getLocalFileName()+"]");      //返回值
*/     }
	

	@Override
	public Object signQuery(BaseMessage baseMsg) throws GopayException {
		return null;
	}

	public SocketTransport getSocketTransport() {
		return socketTransport;
	}

	public void setSocketTransport(SocketTransport socketTransport) {
		this.socketTransport = socketTransport;
	}

	public CryptoHelper getSpdbCipher() {
		return spdbCipher;
	}

	public void setSpdbCipher(CryptoHelper spdbCipher) {
		this.spdbCipher = spdbCipher;
	}
	
	
	
	

}
